package com.zt.questionnaire.common.utils.sign;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

/**
 * AES加密模式：ECB/CBC/CTR/OFB/CFB
 * 填充：pkcs5padding/pkcs7padding/zeropadding/iso10126/ansix923 数据块：128位/192位/256位
 * 密码：【设置加解密的密码，JAVA中有效密码为16位/24位/32位， 其中24位/32位需要JCE（Java 密码扩展无限制权限策略文件，
 * 每个JDK版本对应一个JCE，百度即可找到）】 偏移量：【iv偏移量，ECB不用设置】 输出：base64/hex
 * 字符集：gb2312/gbk/gb18030/utf8
 *
 * 确保以上元素相互匹配，即可保证AES加解密无误。 JAVA代码实现
 *
 * 注意：建议加密密码为16位，避免密码位数不足补0，导致密码不一致，加解密错误。 IOS可设置任意长度的加密密码，JAVA只支持16位/24位/32位
 *
 * @author sy
 *
 */
public class AES_ECB_Utils {

	private static final Logger log = LoggerFactory.getLogger(AES_ECB_Utils.class);

	/**
	 * 加密算法
	 */
	private static final String ENCRY_ALGORITHM = "AES";

	/**
	 * 加密算法/加密模式/填充类型 本例采用AES加密，ECB加密模式，PKCS5Padding填充
	 */
	private static final String CIPHER_MODE = "AES/ECB/PKCS5Padding";

	/**
	 * 设置iv偏移量 本例采用ECB加密模式，不需要设置iv偏移量
	 */
	private static final String IV_ = null;

	/**
	 * 设置加密字符集 本例采用 UTF-8 字符集
	 */
	private static final String CHARACTER = "UTF-8";

	/**
	 * 设置加密密码处理长度。 不足此长度补0；
	 */
	private static final int PWD_SIZE = 16;

	/**
	 * 密码处理方法 如果加解密出问题， 请先查看本方法，排除密码长度不足补"0",导致密码不一致
	 *
	 * @param password 待处理的密码
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private static byte[] pwdHandler(String password) throws UnsupportedEncodingException {
		byte[] data = null;
		if (password == null) {
			password = "";
		}
		StringBuffer sb = new StringBuffer(PWD_SIZE);
		sb.append(password);
		while (sb.length() < PWD_SIZE) {
			sb.append("0");
		}
		if (sb.length() > PWD_SIZE) {
			sb.setLength(PWD_SIZE);
		}

		data = sb.toString().getBytes("UTF-8");

		return data;
	}

	// ======================>原始加密<======================

	/**
	 * 原始加密
	 *
	 * @param clearTextBytes 明文字节数组，待加密的字节数组
	 * @param pwdBytes       加密密码字节数组
	 * @return 返回加密后的密文字节数组，加密错误返回null
	 */
	public static byte[] encrypt(byte[] clearTextBytes, byte[] pwdBytes) {
		try {
			// 1 获取加密密钥
			SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);

			// 2 获取Cipher实例
			Cipher cipher = Cipher.getInstance(CIPHER_MODE);

			// 查看数据块位数 默认为16（byte） * 8 =128 bit
			// System.out.println("数据块位数(byte)：" + cipher.getBlockSize());

			// 3 初始化Cipher实例。设置执行模式以及加密密钥
			cipher.init(Cipher.ENCRYPT_MODE, keySpec);

			// 4 执行
			byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);

			// 5 返回密文字符集
			return cipherTextBytes;

		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 原始解密
	 *
	 * @param cipherTextBytes 密文字节数组，待解密的字节数组
	 * @param pwdBytes        解密密码字节数组
	 * @return 返回解密后的明文字节数组，解密错误返回null
	 */
	public static byte[] decrypt(byte[] cipherTextBytes, byte[] pwdBytes) {

		try {
			// 1 获取解密密钥
			SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);

			// 2 获取Cipher实例
			Cipher cipher = Cipher.getInstance(CIPHER_MODE);

			// 查看数据块位数 默认为16（byte） * 8 =128 bit
			// System.out.println("数据块位数(byte)：" + cipher.getBlockSize());

			// 3 初始化Cipher实例。设置执行模式以及加密密钥
			cipher.init(Cipher.DECRYPT_MODE, keySpec);

			// 4 执行
			byte[] clearTextBytes = cipher.doFinal(cipherTextBytes);

			// 5 返回明文字符集
			return clearTextBytes;

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 解密错误 返回null
		return null;
	}

	// ======================>BASE64<======================

	/**
	 * BASE64加密
	 *
	 * @param clearText 明文，待加密的内容
	 * @param password  密码，加密的密码
	 * @return 返回密文，加密后得到的内容。加密错误返回null
	 */
	public static String encryptBase64(String clearText, String password) {
		try {
			// 1 获取加密密文字节数组
			byte[] cipherTextBytes = encrypt(clearText.getBytes(CHARACTER), pwdHandler(password));

			// 2 对密文字节数组进行BASE64 encoder 得到 BASE6输出的密文
			BASE64Encoder base64Encoder = new BASE64Encoder();
			String cipherText = base64Encoder.encode(cipherTextBytes);

			// 3 返回BASE64输出的密文
			return cipherText;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 加密错误 返回null
		return null;
	}

	/**
	 * BASE64解密
	 *
	 * @param cipherText 密文，带解密的内容
	 * @param password   密码，解密的密码
	 * @return 返回明文，解密后得到的内容。解密错误返回null
	 */
	public static String decryptBase64(String cipherText, String password) {
		try {
			// 1 对 BASE64输出的密文进行BASE64 decodebuffer 得到密文字节数组
			BASE64Decoder base64Decoder = new BASE64Decoder();
			byte[] cipherTextBytes = base64Decoder.decodeBuffer(cipherText);

			// 2 对密文字节数组进行解密 得到明文字节数组
			byte[] clearTextBytes = decrypt(cipherTextBytes, pwdHandler(password));

			// 3 根据 CHARACTER 转码，返回明文字符串
			return new String(clearTextBytes, CHARACTER);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 解密错误返回null
		return null;
	}

	// ======================>HEX<======================

	/**
	 * HEX加密
	 *
	 * @param clearText 明文，待加密的内容
	 * @param password  密码，加密的密码
	 * @return 返回密文，加密后得到的内容。加密错误返回null
	 */
	public static String encryptHex(String clearText, String password) {
		try {
			// 1 获取加密密文字节数组
			byte[] cipherTextBytes = encrypt(clearText.getBytes(CHARACTER), pwdHandler(password));

			// 2 对密文字节数组进行 转换为 HEX输出密文
			String cipherText = byte2hex(cipherTextBytes);

			// 3 返回 HEX输出密文
			return cipherText;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 加密错误返回null
		return null;
	}

	/**
	 * HEX解密
	 *
	 * @param cipherText 密文，带解密的内容
	 * @param password   密码，解密的密码
	 * @return 返回明文，解密后得到的内容。解密错误返回null
	 */
	public static String decryptHex(String cipherText, String password) {
		try {
			// 1 将HEX输出密文 转为密文字节数组
			byte[] cipherTextBytes = hex2byte(cipherText);

			// 2 将密文字节数组进行解密 得到明文字节数组
			byte[] clearTextBytes = decrypt(cipherTextBytes, pwdHandler(password));

			// 3 根据 CHARACTER 转码，返回明文字符串
			return new String(clearTextBytes, CHARACTER);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 解密错误返回null
		return null;
	}

	/* 字节数组转成16进制字符串 */
	public static String byte2hex(byte[] bytes) { // 一个字节的数，
		StringBuffer sb = new StringBuffer(bytes.length * 2);
		String tmp = "";
		for (int n = 0; n < bytes.length; n++) {
			// 整数转成十六进制表示
			tmp = (Integer.toHexString(bytes[n] & 0XFF));
			if (tmp.length() == 1) {
				sb.append("0");
			}
			sb.append(tmp);
		}
		return sb.toString().toUpperCase(); // 转成大写
	}

	/* 将hex字符串转换成字节数组 */
	private static byte[] hex2byte(String str) {
		if (str == null || str.length() < 2) {
			return new byte[0];
		}
		str = str.toLowerCase();
		int l = str.length() / 2;
		byte[] result = new byte[l];
		for (int i = 0; i < l; ++i) {
			String tmp = str.substring(2 * i, 2 * i + 2);
			result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
		}
		return result;
	}

	public static byte[] hexStringToBytes(String hexString) {
		if (hexString == null || hexString.equals("")) {
			return null;
		}
		hexString = hexString.toUpperCase();
		int length = hexString.length() / 2;
		char[] hexChars = hexString.toCharArray();
		byte[] d = new byte[length];
		for (int i = 0; i < length; i++) {
			int pos = i * 2;
			d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
		}
		return d;
	}

	private static byte charToByte(char c) {
		return (byte) "0123456789ABCDEF".indexOf(c);
	}

	public static void main2(String[] args) throws Exception {

		/*
		 * 此处使用AES-128-ECB加密模式，key需要为16位。
		 */

		String keyStr = "72d25f30aefc6aff60b087882cf58972";
		String unSignedStr = "state=paid&timestamp=1551228791ZHANGZHONGYIB201902272";

		log.debug("unsigned str is {}", unSignedStr);
		Encoder encoder = Base64.getEncoder();
		String base64Str = encoder.encodeToString(unSignedStr.getBytes());
		log.debug("base64 String is {}", base64Str);
		byte[] unencrypted = MD5.MD5Byte(base64Str, "utf-8");
		log.debug("md5 digest string is {}", unencrypted);

		byte[] keyBytes = MD5.hex2byte(keyStr);
		log.debug("key bytes is {}", keyBytes);
		SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
		// Cipher cipher = Cipher.getInstance("AES/CBC/ISO10126Padding");
		Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
		// cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new
		// byte[16]));
		cipher.init(Cipher.ENCRYPT_MODE, secretKey);
		byte[] encrypted = cipher.doFinal(unencrypted);
		String base64Encrypted = encoder.encodeToString(encrypted);
		log.debug(base64Encrypted);

	}

	public static void main1(String[] args) throws Exception {

		/*
		 * 此处使用AES-128-ECB加密模式，key需要为16位。
		 */

		String cKey = "327e9f30aefc6a9591a087882cf98032";

		// 需要加密的字串

		String cSrc = "b3JkZXJJZD01MDAxJmNhcnJpZXJPcmRlcklkPTY2ODM3NDM3Mjg5MiZjYXJyaWVyQ29tcGFueT0yMDAwMDImdGltZXN0YW1wPTE0OTcwMDQzMzBaSEFOR1pIT05HWUlCMjAxNzA2MDcy";

		System.out.println(cSrc);// 加密

		String enString = encryptBase64(cSrc, cKey);

		System.out.println("加密后的字串是:" + enString);

		// 解密

		String DeString = decryptBase64(enString, cKey);

		System.out.println("解密后的字串是:" + DeString);

	}

	private static Cipher cipher = null; // 解码器

	public static void main(String[] args) throws Exception {
		String req_info = "p34u/mlxbw6uHFCp4ycVscrgNng+PdrBHH2ow/zHhCZP5YRezIX4D7qJgi0EmkvKy4eJAPRts9DPbgJ4bvoQ9cIBqNSCfQoBb+SXssw0OLO5tu+vx8RNARl7wnFG1WOYl6d1rI4/7rYvuAXkONq3hWyW4CJk4N3x/hIFZFvsdeeDJP6cDGviwxiXGU1sVYe2blwdwtDEQPkJa9F28uHJ5zCawWmK2aKZrK79TlPgJ2XUkEOmNepjP8AGRNcJwy6b5kenD258MLpubHrnBeJ7qd+INsjukr1yNJL+3joQoQKXsfbV83sOagjgifZEIGgG4tVIZ5ApKPLeplwny/1vCefa6mHjDF9cc81hc5xlOvqFwCNuhBBqkk7JJpGHU5ZhvswnD2sq7yKxFmvGg94oFmUuY05/HK5bsD/rIZQLr98AV63E6cHIDir0GFdS3NroroHGH/DekZAOXK983SOUNUpSF7DTKwQ2j/zzXIbgirKkUhB/AYa/LfrsvfpIkxb77aOyekrY0xe9iJJB/ZtnIY+l418tMwXnF7wc6OiJiwWY0yMg0S/e/FDmmsnXqJP/qUH28kqgEQcP5qiuAXr+T/Kqy/k/af9MKqwtYrPFdrCiUiO4uP4gaJ2PnMEqPStH4/ZejsN7+MniZFeychUHkMkkMymQqla+Qmq05Fi/MInhNyhQj3YHU/EFC+916gPzaPnBBblXnWb9ye71zUuZeexDhzkA529fMEEOkCIT6D0XRyyQKer6sXgGxgkH8Zo2RZyptj9ns9Q79jaDAULAJTB01za3M+pLg3ClmXLmB1KF2BnbFVUyQdE92deFLYHx6udzyfASNnYEVFpxCpFIHNatedEyiYlTns9XZlOF8WI94EyNb/fWgwEYd2PTxAskscTWtk75Kk55Z9m5B6gS2tE8GLiwDsAp2n+Sx/IGzy7jsaUc9dOQevPqrLeaLfIDP5MiJegmcVbh3mFcHr7bQatuvlazFjMdLI+8/2FE/byc40mmgPu6ppgH7AXw8Q5zY9y9gJkqmeqZg2+lzM97ovG1yMa246thTMocrPB5wyQ=";

//		init(mchKey);

		Decoder decoder = Base64.getDecoder();
		byte[] base64ByteArr = decoder.decode(req_info);

		String result = new String(cipher.doFinal(base64ByteArr));
		System.out.println("解密结果:{}" + result);
	}

	public static String decryptData(String reqInfo, String mchKey) {
		init(mchKey);
		Decoder decoder = Base64.getDecoder();
		byte[] base64ByteArr = decoder.decode(reqInfo);

		String result;
		try {
			result = new String(cipher.doFinal(base64ByteArr));
			return result;
		} catch (Exception e) {
			log.error("解析req_info失败 {}",  e);
			return null;
		}
	}

	public static void init(String mchKey) {
		Security.addProvider(new BouncyCastleProvider());
		String key = MD5.MD5Encode(mchKey).toLowerCase();
		System.out.println(key);
		SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
		Security.addProvider(new BouncyCastleProvider());
		try {
			cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
			cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		}
	}

}
